<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>roles/roles_server.js - The meteor-roles API</title>
    <link rel="stylesheet" href="http://yui.yahooapis.com/3.9.1/build/cssgrids/cssgrids-min.css">
    <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
    <link rel="stylesheet" href="../assets/css/main.css" id="site_styles">
    <link rel="icon" href="../assets/favicon.ico">
    <script src="http://yui.yahooapis.com/combo?3.9.1/build/yui/yui-min.js"></script>
</head>
<body class="yui3-skin-sam">

<div id="doc">
    <div id="hd" class="yui3-g header">
        <div class="yui3-u-3-4">
                <h1><img src="../assets/css/logo.png" title="The meteor-roles API" width="117" height="52"></h1>
        </div>
        <div class="yui3-u-1-4 version">
            <em>API Docs for: v1.2.14</em>
        </div>
    </div>
    <div id="bd" class="yui3-g">

        <div class="yui3-u-1-4">
            <div id="docs-sidebar" class="sidebar apidocs">
                <div id="api-list">
                    <h2 class="off-left">APIs</h2>
                    <div id="api-tabview" class="tabview">
                        <ul class="tabs">
                            <li><a href="#api-classes">Classes</a></li>
                            <li><a href="#api-modules">Modules</a></li>
                        </ul>
                
                        <div id="api-tabview-filter">
                            <input type="search" id="api-filter" placeholder="Type to filter APIs">
                        </div>
                
                        <div id="api-tabview-panel">
                            <ul id="api-classes" class="apis classes">
                                <li><a href="../classes/Roles.html">Roles</a></li>
                                <li><a href="../classes/UIHelpers.html">UIHelpers</a></li>
                            </ul>
                
                
                            <ul id="api-modules" class="apis modules">
                                <li><a href="../modules/Roles.html">Roles</a></li>
                                <li><a href="../modules/UIHelpers.html">UIHelpers</a></li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="yui3-u-3-4">
                <div id="api-options">
                    Show:
                    <label for="api-show-inherited">
                        <input type="checkbox" id="api-show-inherited" checked>
                        Inherited
                    </label>
            
                    <label for="api-show-protected">
                        <input type="checkbox" id="api-show-protected">
                        Protected
                    </label>
            
                    <label for="api-show-private">
                        <input type="checkbox" id="api-show-private">
                        Private
                    </label>
                    <label for="api-show-deprecated">
                        <input type="checkbox" id="api-show-deprecated">
                        Deprecated
                    </label>
            
                </div>
            
            <div class="apidocs">
                <div id="docs-main">
                    <div class="content">
<h1 class="file-heading">File: roles/roles_server.js</h1>

<div class="file">
    <pre class="code prettyprint linenums">
&quot;use strict&quot;;

// Create default indexes on users collection.
// Index only on &quot;roles._id&quot; is not needed because the combined index works for it as well.
Meteor.users._ensureIndex({&#x27;roles._id&#x27;: 1, &#x27;roles.scope&#x27;: 1});
Meteor.users._ensureIndex({&#x27;roles.scope&#x27;: 1});

/*
 * Publish logged-in user&#x27;s roles so client-side checks can work.
 *
 * Use a named publish function so clients can check &#x60;ready()&#x60; state.
 */
Meteor.publish(&#x27;_roles&#x27;, function () {
  var loggedInUserId = this.userId,
      fields = {roles: 1};

  if (!loggedInUserId) {
    this.ready();
    return;
  }

  return Meteor.users.find({_id: loggedInUserId},
                           {fields: fields});
});

_.extend(Roles, {
  /**
   * @method _isNewRole
   * @param {Object} role &#x60;Meteor.roles&#x60; document.
   * @return {Boolean} Returns &#x60;true&#x60; if the &#x60;role&#x60; is in the new format.
   *                   If it is ambiguous or it is not, returns &#x60;false&#x60;.
   * @for Roles
   * @private
   * @static
   */
  _isNewRole: function (role) {
    return !_.has(role, &#x27;name&#x27;) &amp;&amp; _.has(role, &#x27;children&#x27;);
  },

  /**
   * @method _isOldRole
   * @param {Object} role &#x60;Meteor.roles&#x60; document.
   * @return {Boolean} Returns &#x60;true&#x60; if the &#x60;role&#x60; is in the old format.
   *                   If it is ambiguous or it is not, returns &#x60;false&#x60;.
   * @for Roles
   * @private
   * @static
   */
  _isOldRole: function (role) {
    return _.has(role, &#x27;name&#x27;) &amp;&amp; !_.has(role, &#x27;children&#x27;);
  },

  /**
   * @method _isNewField
   * @param {Array} roles &#x60;Meteor.users&#x60; document &#x60;roles&#x60; field.
   * @return {Boolean} Returns &#x60;true&#x60; if the &#x60;roles&#x60; field is in the new format.
   *                   If it is ambiguous or it is not, returns &#x60;false&#x60;.
   * @for Roles
   * @private
   * @static
   */
  _isNewField: function (roles) {
    return _.isArray(roles) &amp;&amp; _.isObject(roles[0]);
  },

  /**
   * @method _isOldField
   * @param {Array} roles &#x60;Meteor.users&#x60; document &#x60;roles&#x60; field.
   * @return {Boolean} Returns &#x60;true&#x60; if the &#x60;roles&#x60; field is in the old format.
   *                   If it is ambiguous or it is not, returns &#x60;false&#x60;.
   * @for Roles
   * @private
   * @static
   */
  _isOldField: function (roles) {
    return (_.isArray(roles) &amp;&amp; _.isString(roles[0])) || (_.isObject(roles) &amp;&amp; !_.isArray(roles));
  },

  /**
   * @method _convertToNewRole
   * @param {Object} oldRole &#x60;Meteor.roles&#x60; document.
   * @return {Object} Converted &#x60;role&#x60; to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewRole: function (oldRole) {
    if (!_.isString(oldRole.name)) throw new Error(&quot;Role name &#x27;&quot; + oldRole.name + &quot;&#x27; is not a string.&quot;);

    return {
      _id: oldRole.name,
      children: []
    };
  },

  /**
   * @method _convertToOldRole
   * @param {Object} newRole &#x60;Meteor.roles&#x60; document.
   * @return {Object} Converted &#x60;role&#x60; to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldRole: function (newRole) {
    if (!_.isString(newRole._id)) throw new Error(&quot;Role name &#x27;&quot; + newRole._id + &quot;&#x27; is not a string.&quot;);

    return {
      name: newRole._id
    };
  },

  /**
   * @method _convertToNewField
   * @param {Array} oldRoles &#x60;Meteor.users&#x60; document &#x60;roles&#x60; field in the old format.
   * @param {Boolean} convertUnderscoresToDots Should we convert underscores to dots in group names.
   * @return {Array} Converted &#x60;roles&#x60; to the new format.
   * @for Roles
   * @private
   * @static
   */
  _convertToNewField: function (oldRoles, convertUnderscoresToDots) {
    var roles = [];
    if (_.isArray(oldRoles)) {
      _.each(oldRoles, function (role, index) {
        if (!_.isString(role)) throw new Error(&quot;Role &#x27;&quot; + role + &quot;&#x27; is not a string.&quot;);

        roles.push({
          _id: role,
          scope: null,
          assigned: true
        })
      });
    }
    else if (_.isObject(oldRoles)) {
      _.each(oldRoles, function (rolesArray, group) {
        if (group === &#x27;__global_roles__&#x27;) {
          group = null;
        }
        else if (convertUnderscoresToDots) {
          // unescape
          group = group.replace(/_/g, &#x27;.&#x27;);
        }

        _.each(rolesArray, function (role, index) {
          if (!_.isString(role)) throw new Error(&quot;Role &#x27;&quot; + role + &quot;&#x27; is not a string.&quot;);

          roles.push({
            _id: role,
            scope: group,
            assigned: true
          })
        });
      })
    }
    return roles;
  },

  /**
   * @method _convertToOldField
   * @param {Array} newRoles &#x60;Meteor.users&#x60; document &#x60;roles&#x60; field in the new format.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @return {Array} Converted &#x60;roles&#x60; to the old format.
   * @for Roles
   * @private
   * @static
   */
  _convertToOldField: function (newRoles, usingGroups) {
    var roles;

    if (usingGroups) {
      roles = {};
    }
    else {
      roles = [];
    }

    _.each(newRoles, function (userRole, index) {
      if (!_.isObject(userRole)) throw new Error(&quot;Role &#x27;&quot; + userRole + &quot;&#x27; is not an object.&quot;);

      // We assume that we are converting back a failed migration, so values can only be
      // what were valid values in 1.0. So no group names starting with $ and no subroles.

      if (userRole.scope) {
        if (!usingGroups) throw new Error(&quot;Role &#x27;&quot; + userRole._id + &quot;&#x27; with scope &#x27;&quot; + userRole.scope + &quot;&#x27; without enabled groups.&quot;);

        // escape
        var scope = userRole.scope.replace(/\./g, &#x27;_&#x27;);

        if (scope[0] === &#x27;$&#x27;) throw new Error(&quot;Group name &#x27;&quot; + scope + &quot;&#x27; start with $.&quot;);

        roles[scope] = roles[scope] || [];
        roles[scope].push(userRole._id);
      }
      else {
        if (usingGroups) {
          roles.__global_roles__ = roles.__global_roles__ || [];
          roles.__global_roles__.push(userRole._id);
        }
        else {
          roles.push(userRole._id);
        }
      }
    });
    return roles;
  },

  /**
   * @method _defaultUpdateUser
   * @param {Object} user &#x60;Meteor.users&#x60; document.
   * @param {Array|Object} roles Value to which user&#x27;s &#x60;roles&#x60; field should be set.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateUser: function (user, roles) {
    Meteor.users.update({
      _id: user._id,
      // making sure nothing changed in meantime
      roles: user.roles
    }, {
      $set: {roles: roles}
    });
  },

  /**
   * @method _defaultUpdateRole
   * @param {Object} oldRole Old &#x60;Meteor.roles&#x60; document.
   * @param {Object} newRole New &#x60;Meteor.roles&#x60; document.
   * @for Roles
   * @private
   * @static
   */
  _defaultUpdateRole: function (oldRole, newRole) {
    Meteor.roles.remove(oldRole._id);
    Meteor.roles.insert(newRole);
  },

  /**
   * @method _dropCollectionIndex
   * @param {Object} collection Collection on which to drop the index.
   * @param {String} indexName Name of the index to drop.
   * @for Roles
   * @private
   * @static
   */
  _dropCollectionIndex: function (collection, indexName) {
    try {
      collection._dropIndex(indexName);
    } catch (e) {
      if (e.name !== &#x27;MongoError&#x27;) throw e;
      if (!/index not found/.test(e.err || e.errmsg)) throw e;
    }
  },

  /**
   * Migrates &#x60;Meteor.users&#x60; and &#x60;Meteor.roles&#x60; to the new format.
   *
   * @method _forwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default &#x60;_defaultUpdateUser&#x60;.
   * @param {Function} updateRole Function which updates the role object. Default &#x60;_defaultUpdateRole&#x60;.
   * @param {Boolean} convertUnderscoresToDots Should we convert underscores to dots in group names.
   * @for Roles
   * @private
   * @static
   */
  _forwardMigrate: function (updateUser, updateRole, convertUnderscoresToDots) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    Roles._dropCollectionIndex(Meteor.roles, &#x27;name_1&#x27;);

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isNewRole(role)) {
        updateRole(role, Roles._convertToNewRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isNewField(user.roles)) {
        updateUser(user, Roles._convertToNewField(user.roles, convertUnderscoresToDots));
      }
    });
  },

  /**
   * Migrates &#x60;Meteor.users&#x60; and &#x60;Meteor.roles&#x60; to the old format.
   *
   * We assume that we are converting back a failed migration, so values can only be
   * what were valid values in the old format. So no group names starting with &#x60;$&#x60; and
   * no subroles.
   *
   * @method _backwardMigrate
   * @param {Function} updateUser Function which updates the user object. Default &#x60;_defaultUpdateUser&#x60;.
   * @param {Function} updateRole Function which updates the role object. Default &#x60;_defaultUpdateRole&#x60;.
   * @param {Boolean} usingGroups Should we use groups or not.
   * @for Roles
   * @private
   * @static
   */
  _backwardMigrate: function (updateUser, updateRole, usingGroups) {
    updateUser = updateUser || Roles._defaultUpdateUser;
    updateRole = updateRole || Roles._defaultUpdateRole;

    Roles._dropCollectionIndex(Meteor.users, &#x27;roles._id_1_roles.scope_1&#x27;);
    Roles._dropCollectionIndex(Meteor.users, &#x27;roles.scope_1&#x27;);

    Meteor.roles.find().forEach(function (role, index, cursor) {
      if (!Roles._isOldRole(role)) {
        updateRole(role, Roles._convertToOldRole(role));
      }
    });

    Meteor.users.find().forEach(function (user, index, cursor) {
      if (!Roles._isOldField(user.roles)) {
        updateUser(user, Roles._convertToOldField(user.roles, usingGroups));
      }
    });
  }
});

    </pre>
</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="../assets/vendor/prettify/prettify-min.js"></script>
<script>prettyPrint();</script>
<script src="../assets/js/yui-prettify.js"></script>
<script src="../assets/../api.js"></script>
<script src="../assets/js/api-filter.js"></script>
<script src="../assets/js/api-list.js"></script>
<script src="../assets/js/api-search.js"></script>
<script src="../assets/js/apidocs.js"></script>
</body>
</html>
